package com.mai4j.maiadmin.support.security.aop;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * 微信公众号请关注: 开源螺丝钉
 * <br>
 * 码云请关注: <a href="https://gitee.com/xiyoufang">https://gitee.com/xiyoufang</a>
 * <br>
 * 哔哩哔哩请关注: <a href="https://space.bilibili.com/438927834">https://space.bilibili.com/438927834</a>
 *
 * @author xiyoufang
 */
public abstract class AuthorizingAnnotationMethodInterceptor {

    protected AuthorizingAnnotationHandler handler;

    public AuthorizingAnnotationMethodInterceptor(AuthorizingAnnotationHandler handler) {
        this.handler = handler;
    }

    public void assertAuthorized(MethodInvocation mi) throws AccessDeniedException {
        try {
            handler.assertAuthorized(getAnnotation(mi, handler.getAnnotationClass()), mi);
        } catch (AccessDeniedException ae) {
            throw new AccessDeniedException("Not authorized to invoke method: " + mi.getMethod());
        }
    }

    public boolean supports(MethodInvocation mi) {
        return getAnnotation(mi, handler.getAnnotationClass()) != null;
    }

    private Annotation getAnnotation(MethodInvocation mi, Class<? extends Annotation> clazz) {
        Method m = mi.getMethod();
        Annotation a = AnnotationUtils.findAnnotation(m, clazz);
        if (a != null) return a;
        Object aThis = mi.getThis();
        if (aThis == null) return null;
        Class<?> targetClass = aThis.getClass();
        m = ClassUtils.getMostSpecificMethod(m, targetClass);
        a = AnnotationUtils.findAnnotation(m, clazz);
        if (a != null) return a;
        return AnnotationUtils.findAnnotation(aThis.getClass(), clazz);
    }
}
